package com.smsc.headend.task.engine.executor.impl.firmware;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import com.smsc.headend.common.enums.DeviceType;
import com.smsc.headend.common.enums.errorcode.TaskExecuteErrorCode;
import com.smsc.headend.module.asset.dmo.ComConfigDMO;
import com.smsc.headend.module.asset.dmo.MeterConfigDMO;
import com.smsc.headend.module.asset.entity.ComEntity;
import com.smsc.headend.module.asset.entity.Meter;
import com.smsc.headend.module.common.entity.DeviceOperation;
import com.smsc.headend.module.data.dmo.DeviceSAPandKeys;
import com.smsc.headend.module.data.dto.DataItemDTO;
import com.smsc.headend.module.data.enums.DictionaryType;
import com.smsc.headend.module.data.enums.OperationGroup;
import com.smsc.headend.module.fwu.entity.LogFwUpgradeHis;
import com.smsc.headend.module.fwu.entity.TemplateFwUpgrade;
import com.smsc.headend.module.protocol.cosem.util.ByteUtils;
import com.smsc.headend.module.task.dto.AtomicTask;
import com.smsc.headend.module.task.dto.AtomicTaskState;
import com.smsc.headend.module.task.dto.Task;
import com.smsc.headend.task.engine.dao.TaskDAO;
import com.smsc.headend.task.engine.exception.UDISTaskExecutorException;
import com.smsc.headend.task.engine.executor.TaskExecutor;
import com.smsc.headend.task.engine.facade.DataItemFacade;
import com.smsc.headend.task.engine.handler.impl.firmware.ReadFirmwareVersionConfirmHandler;
import com.smsc.headend.task.engine.handler.impl.firmware.ReadFirmwareVersionHandler;
import com.smsc.headend.task.engine.mapper.LogFwUpgradeHisMapper;
import com.smsc.headend.task.engine.service.ConnectorService;
import com.smsc.headend.task.engine.service.FeignAssetManagementService;
import com.smsc.headend.task.engine.service.asset.AssetService;
import com.smsc.headend.task.engine.service.firmware.FirmwareService;
import com.smsc.headend.task.engine.service.task.TaskPacketService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;

import java.util.List;

/**
 * @author liangli
 * @date 2020/11/10
 */
@Slf4j
public class ReadFirmwareVersionExecutor implements TaskExecutor {

    @Autowired
    FeignAssetManagementService assetManagementRemoteService;
    @Autowired
    DataItemFacade dataItemFacade;
    @Autowired
    TaskPacketService packetService;
    @Autowired
    ConnectorService connectorService;
    @Autowired
    AssetService assetService;
    @Autowired
    TaskDAO taskDAO;
    @Autowired
    FirmwareService firmwareService;
    @Autowired
    LogFwUpgradeHisMapper logFwUpgradeHisMapper;

    @Override
    public AtomicTaskState execute(Task task, AtomicTask atomicTask) throws UDISTaskExecutorException {
        DeviceOperation dictionary = assetService.getDictionary(atomicTask.getOperationId(), DictionaryType.Operation.getType());
        Meter meter = getMeter(task, assetService);
        MeterConfigDMO meterConfigDMO = task.getMeterId() == null ? null : assetService.getMeterConfigById(task.getMeterId());
        ComEntity terminal = assetService.getComById(task.getComId());
        ComConfigDMO comConfigDMO = assetService.getComConfigById(task.getComId());

        firmwareTaskExecutable(task, atomicTask, terminal);

        List<DataItemDTO> dataItemCosemList = dataItemFacade.getDataItemsByOperation(meter, terminal, dictionary, getParameterMap(task, atomicTask), comConfigDMO.getModelId());
        if (CollectionUtils.isEmpty(dataItemCosemList)) {
            throw new UDISTaskExecutorException(TaskExecuteErrorCode.DATAITEM_NOT_EXISTS.toString());
        }
        Boolean isReadFromTerminal = isReadFromTerminal(task);
        DataItemDTO dataItemDto = dataItemCosemList.get(0);
        DeviceSAPandKeys deviceKeys = meter != null ? assetManagementRemoteService.getMeterCommunicationKey(meter.getMeterId(), dataItemDto.getRoleId()) :
                assetManagementRemoteService.getTerminalCommunicationKey(terminal.getComId(), dataItemDto.getRoleId());

        Long fwTypeId = Convert.toLong(this.getParameterMap(task, atomicTask).get("fwTypeId"));
        //SMOD模块读取加入表信息
        byte[] packets;
        if (null != fwTypeId && fwTypeId == 10L) {
            packets = packetService.buildReadOneAttributePacketWithMfgSn(dataItemCosemList.get(0), meter, meterConfigDMO, deviceKeys);
            //read from dcu need to set Connector Task meter Id to Null
            task.setMeterId(null);
        } else {
            packets = packetService.buildReadOneAttributePacket(dataItemCosemList.get(0), isReadFromTerminal ? comConfigDMO : meterConfigDMO, deviceKeys);
        }
        log.info(ByteUtils.byteToHexString(packets));
        connectorService.sendPacket(task, atomicTask, dataItemCosemList.get(0).getRoleId(), true, packets);
        //set meterId back
        if (null != meter) {
            task.setMeterId(meter.getMeterId());
        }
        AtomicTaskState state = taskDAO.hashGetAtomicTaskState(atomicTask.getAtomicTaskNo());
        if (state != null) {
            state.setStored(true);
            return state;
        }

        return createAtomicState(OperationGroup.FirmwareVersionVerify.getId() == atomicTask.getGroupId().intValue()
                ? ReadFirmwareVersionConfirmHandler.class : ReadFirmwareVersionHandler.class);
    }

    private void firmwareTaskExecutable(Task task, AtomicTask atomicTask, ComEntity comEntity) throws UDISTaskExecutorException {
        //确认版本需要确认之前的激活操作已经完成
        if (OperationGroup.FirmwareVersionVerify.getId() == atomicTask.getGroupId().intValue()) {
            Long deviceType;
            Long deviceId;
            if (task.getMeterId() != null) {
                deviceType = DeviceType.METER.getId();
                deviceId = task.getMeterId();
            } else {
                deviceType = comEntity.getComDeviceType();
                deviceId = comEntity.getDeviceId();
            }

            Long taskPid = MapUtil.getLong(getParameterMap(task, atomicTask), "taskPid");
            if (taskPid == null) {
                throw new UDISTaskExecutorException(TaskExecuteErrorCode.FW_UP_TASK_PID_NOT_EXISTS.toString());
            }
            List<LogFwUpgradeHis> list = assetManagementRemoteService.getFwuReportList(deviceId, deviceType, taskPid);
            if (CollUtil.isEmpty(list)) {
                throw new UDISTaskExecutorException(TaskExecuteErrorCode.FW_UP_NOT_COMPLETE.toString());
            }
            LogFwUpgradeHis activationReport = list.stream().filter(e -> e.getTaskType() == TemplateFwUpgrade.TaskType.ImageActivation.getCode()).findFirst().orElse(null);
            if (activationReport == null) {
                activationReport = list.stream().filter(e -> e.getTaskType() == TemplateFwUpgrade.TaskType.FtpSettings.getCode()).findFirst().orElse(null);
            }
            if (activationReport == null || activationReport.getUpgradeResult() != LogFwUpgradeHis.UpgradeResult.success.getCode()) {
                throw new UDISTaskExecutorException(TaskExecuteErrorCode.FW_UP_NOT_COMPLETE.toString());
            }
            if (TemplateFwUpgrade.TaskType.FtpSettings.getCode() == activationReport.getTaskType()) {
                if (LogFwUpgradeHis.PushStatus.failed.getCode() == activationReport.getPushStatus() || LogFwUpgradeHis.PushStatus.success.getCode() == activationReport.getPushStatus()) {
                    throw new UDISTaskExecutorException(TaskExecuteErrorCode.FW_UP_NOT_COMPLETE.toString());
                } else if (LogFwUpgradeHis.PushStatus.not_pushed.getCode() == activationReport.getPushStatus()) {
                    activationReport.setPushStatus(LogFwUpgradeHis.PushStatus.expired.getCode());
                    logFwUpgradeHisMapper.updateById(activationReport);
                }
            }
        }
    }

    private Boolean isReadFromTerminal(Task task) {
        if (task.getMeterId() == null) {
            return true;
        }
        return false;
    }

    @Override
    public Boolean handleFailure(Task task, AtomicTask atomicTask, String message) {
        log.info("error {}, ", message);
        if (OperationGroup.FirmwareVersionVerify.getId() == atomicTask.getGroupId().intValue()) {
            if (UDISTaskExecutorException.COM_STATUS_EXCEPTION.equalsIgnoreCase(message)) {
                log.warn("read version pause because of com offline");
                return false;
            }
            firmwareService.saveFwuReport(task, atomicTask, -1, TemplateFwUpgrade.TaskType.ImageTransfer, "read version execute failed", null);
            return true;
        }
        return true;
    }
}

